
package data_queuing;

import thes2sim.Frame;
import thes2sim.*;

import com.aliasi.util.BoundedPriorityQueue;
import java.io.IOException;
import java.util.Comparator;
import java.util.logging.Level;
import java.util.logging.Logger;


public class FairQueuingAlgo {
    
    private Comparator<Frame> comparator = new PriorityComparator();
    BoundedPriorityQueue<Frame> dqHigh;
    BoundedPriorityQueue<Frame> dqMid;
    BoundedPriorityQueue<Frame> dqLow;
    int maxMid = Util.FQAmaxMid;
    int maxLow = Util.FQAmaxLow;
    
    Logging l = new Logging(1);
    int time;
    
    public FairQueuingAlgo () {
        
        dqHigh = new BoundedPriorityQueue<Frame>(comparator, Util.queueSize);
        dqMid = new BoundedPriorityQueue<Frame>(comparator, Util.queueSize);
        dqLow = new BoundedPriorityQueue<Frame>(comparator, Util.queueSize);
        
    }
   
    public void printQContent() {
        
       Frame f;
  
       while(!dqHigh.isEmpty()) {
           f = dqHigh.remove();
           logAction(f, 5);
       }
    
       while(!dqMid.isEmpty()) {
           f = dqMid.remove();
           logAction(f, 6);
       }
    
       while(!dqLow.isEmpty()) {
           f = dqLow.remove();
           logAction(f, 7);
       }
       
    }
    
    // Increments frame counters
    public void incrementCounters() {
        
        if(!dqMid.isEmpty()) {
            dqMid.first().incrementCounter();
        }
        if(!dqLow.isEmpty()) {
            dqLow.first().incrementCounter();
        }
    }
    
    // Checking queue if full and discards frame at tail
    public void checkQueue (BoundedPriorityQueue<Frame> dq) {
        
        if(dq.size() >= dq.maxSize()) {
            Frame discarded = dq.last();
            discarded.setDiscTime(time);
            logAction(discarded, 2);
            dq.remove(discarded);
        }
    
    }
    
    // Determines which queue will transmit
    public int checkCounters() {
        
        int i = 0;

        // Check if both counter is full and decide on time creation
        if(!dqMid.isEmpty() && !dqLow.isEmpty()){
            if((dqMid.first().getCounter() == maxMid )&& (dqLow.first().getCounter() == maxLow)){
                if( dqMid.first().getPayload().getTimecreate() < dqLow.first().getPayload().getTimecreate() ) {
                    return i = 2;
                }
                else {
                    return i = 3;
                }
            }
        }
        
        if(dqHigh.isEmpty()) {
            if(dqMid.isEmpty()){
                if(!dqLow.isEmpty()) {
                    return i = 3;
                }
            }
            else {
                    return i = 2;
            }
        }
        
        // Check if counter is full for Mid
        if(!dqMid.isEmpty() && (dqMid.first().getCounter()) == maxMid){
                return i = 2;
        }

        // Check if counter is full for Low
        else if(!dqLow.isEmpty() && (dqLow.first().getCounter()) == maxLow) {
            return i = 3;
        }
        
        else if(!dqHigh.isEmpty()){
            return i = 1;
        }

        return i;
    
    }
    
    // Returns frame for transmission
    public Frame transmitFrame (int simTime) {
            
        Frame f = null;
        this.time = simTime;

        switch(checkCounters()){
            
            case 1: f = dqHigh.remove();
                    break;
            case 2: f = dqMid.remove();
                    break;
            case 3: f = dqLow.remove();
                    break;
        }
        
        // Set exit time of frame
        if (f != null) {
            f.setExtTime(time);
            logAction(f, 3);
        }
            
        // Increment counters after every transmit
        incrementCounters();
            
        return f; 
            
    }
    
    // Adds frame to queue according to priority
    public void queuePDU (PDU u, int id, int simTime) {
        
        Frame f = new Frame(u, id);
        
        this.time = simTime; 
        f.setEntTime(time);
        
        switch(f.getPayload().getPriority()) {
            
            // High Priority
            case 1: 
                        logAction(f, 1);
                        dqHigh.offer(f);
                        checkQueue(dqHigh);
                        break;
            
            // Mid Priority    
            case 2: 
                        logAction(f, 1);
                        dqMid.offer(f);
                        checkQueue(dqMid);
                        break;
            
            // Low Priority
            case 3:
                        logAction(f, 1);
                        dqLow.offer(f);
                        checkQueue(dqLow);
                        break;
                
        }
        
        
    }
    
    // Sorting frames according to time creation
    private class PriorityComparator implements Comparator<Frame> {

        public int compare(Frame x, Frame y) {
            
            if(x.isServicing()) {
                return 1;
            }
            
            if(x.getPayload().getTimecreate() < y.getPayload().getTimecreate()) {
                return 1;
            }
            
            if(x.getPayload().getTimecreate() > y.getPayload().getTimecreate()) {
                return -1;
            }
            
            return 0;
        }

    }
    
    // 1 - inserted frame
    // 2 - discarded frame
    // 3 - transmitted frame
    // 4, 5, 6, 7 - queue content
  
    public void logAction(Frame f, int type) {
        
        try {
            
            switch(type){
                
                case 1:
                    l.logActivity(
                        time + ", " 
                        + "Frame Insert, " 
                        + f.getId() + ", "
                        + f.getPayload().getPriority() + ", "
                        + f.getPayload().getTimecreate(), 1);

                    l.logActivity(f.getId() + ", " 
                                + f.getPayload().getPriority() + ", " 
                                + f.getEntTime(), 2);
                    break;
                    
                case 2:
                    l.logActivity(
                        time + ", " 
                        + "Frame Discarded, " 
                        + f.getId() + ", "
                        + f.getPayload().getPriority() + ", "
                        + f.getPayload().getTimecreate(), 1);
                    
                    l.logActivity(f.getId() + ", " 
                                + f.getPayload().getPriority() + ", " 
                                + f.getEntTime() + ", " 
                                + f.getDiscTime(), 3);
                    break;
                    
                case 3:
                    l.logActivity(
                        time + ", " 
                        + "Frame Transmitted, " 
                        + f.getId() + ", "
                        + f.getPayload().getPriority() + ", "
                        + f.getPayload().getTimecreate(), 1);
                    
                    l.logActivity(f.getId() + ", " 
                                + f.getPayload().getPriority() + ", " 
                                + f.getEntTime() + ", " 
                                + f.getExtTime(), 4);
                    break;
                    
                //case 4:
                //    l.logActivity(f.getId() + ", " + f.getPayload().getPriority(), 5);
                //    break;
                
                case 5:
                    l.logActivity(f.getId() + ", " 
                                + f.getPayload().getPriority(), 6);
                    break;
                    
                case 6:
                    l.logActivity(f.getId() + ", " 
                                + f.getPayload().getPriority(), 7);
                    break;
                    
                case 7:
                    l.logActivity(f.getId() + ", " 
                                + f.getPayload().getPriority(), 8);
                    break;
                    
            }
            
        } catch (IOException ex) {
            Logger.getLogger(QueuingOrig.class.getName()).log(Level.SEVERE, null, ex);
        }
       

    }
    
    
}
